#Written by MMCOWBOY
import pygame
import math
import weapon
import effect #flame,
import collidet, item, mp_attack

class enemy_obj(pygame.sprite.Sprite):

    SPARK_INTV = 2				                            # enemy trail flame spark interval
    KEEP_DISTANCE = 80				                        # the smallest distance when in tracing movment
    ATTACK_MODE_STRAIGHT_SINGLE = 1                         # attack_mode straight forward & singal ball
    ATTACK_MODE_STRAIGHT_DOUBLE = 2                         # attack_mode straight forward & double balls
    ATTACK_MODE_STRAIGHT_THREE = 3                          # attack_mode straight forward & three balls
    ATTACK_MODE_AIMING_SINGLE = 4                           # attack_mode aiming & singal ball
    ATTACK_MODE_AIMING_NEAREST_SINGLE = 5                   # attack_mode aiming the nearest player & singal ball
    ATTACK_MODE_BOSS_1 = 10                                 # attack_mode for boss 1#
    ATTACK_MODE_BOSS_2 = 11                                 # attack_mode for boss 2#
    MOTION_STRAINGHT = 1                                    # move straight forward
    MOTION_SIN = 2                                          # move controled by sin
    MOTION_MINUS_SIN = 3                                    # move controled by -sin
    MOTION_COS = 4                                          # move controled by cos
    MOTION_MINUS_COS = 5                                    # move controled by -cos
    MOTION_TRACING = 6                                      # Tracing movment
    MOTION_T = 10                                           # 'T' movment
    
    def __init__(self, screen_size, position, sub_type, motion, item_type, players):
        
        super(enemy_obj, self).__init__(self.containers)
        self.screen_size = screen_size		                # screen size
        self.screen_width = screen_size[0]	                # screen width
        self.screen_height = screen_size[1]	                # screen height
        self.sub_type = sub_type		                    # enemy subtype
        self.motion = motion			                    # enemy moving type
        self.attack_mode = 0		                        # enemy attack mode
        self.flame_spark_intv = self.SPARK_INTV             # spark evry SPARK_INTV frame
        self.step_counter = 0                               # count how many step does the obj move
        self.target = None                                  # Target(s) that will be locked/attacked by enemy_obj
        self.isboss = False			                        # boss flag
        self.players = players			                    # player list
        self.angle = 0     				                # image rotate angle
        self.fire_intv = 80		    	                    # enemy fire interval
        self.firing = 0 			                        # enemy fire flag
        self.item_type = item_type                          # trans to item type
        
        self.fire_offset_x = 0
        self.fire_offset_y = 0
        
        #--------------------------------------------
        self.move_step = 60   
        self.is_item = False
        #--------------------------------------------
        self.init_pos = position                            # enemy initial position
        
        if (self.sub_type == 1):
            self.build_enemy_1()
            
        elif (self.sub_type == 2):
            self.build_enemy_2()
            
        elif (self.sub_type == 3):
            self.build_enemy_3()

        elif (self.sub_type == 4):
            self.build_enemy_4()

        elif (self.sub_type == 5):
            self.build_enemy_5()

        elif (self.sub_type == 20):         #boss 1#
            self.build_enemy_20()

        elif (self.sub_type == 21):         #boss 2#
            self.build_enemy_21()
        
        else:
            pass

            
    #---------------------- ATTACK CALLING FUNCTION ------------------------------

    def attack(self):
        if (self.attack_mode):
            if (self.firing == 0):
                self.firing = self.fire_intv
                
                if (self.attack_mode == self.ATTACK_MODE_STRAIGHT_SINGLE):
                    self.attack_strainght_single()
                    
                elif (self.attack_mode == self.ATTACK_MODE_STRAIGHT_DOUBLE):
                    self.attack_strainght_double()
                    
                elif (self.attack_mode == self.ATTACK_MODE_STRAIGHT_THREE):
                    self.attack_strainght_three()

                elif (self.attack_mode == self.ATTACK_MODE_AIMING_SINGLE):
                    self.attack_aiming_single()

                elif (self.attack_mode == self.ATTACK_MODE_AIMING_NEAREST_SINGLE):
                    self.attack_aiming_nearest_single()

                elif (self.attack_mode == self.ATTACK_MODE_BOSS_1):
                    self.attack_boss_1()

                elif (self.attack_mode == self.ATTACK_MODE_BOSS_2):
                    self.attack_boss_2()
                
                else:
                    pass
                
            else:
                self.firing -= 1
                pass

    def get_nearest_player(self):
        mini_dis = -1
        for p in self.players:
            dis = collidet.get_distance(self, p)
            if (mini_dis == -1 or mini_dis > dis):
                mini_dis = dis
                self.target = p

    def get_shot_speed(self, speed = 7.5):
        #-------------------------------------------
        if self.target is None or not self.target.is_alived():
            return [0, speed]
        #--------------------------------------------
        dx = self.rect.centerx - self.target.rect.centerx
        dy = self.rect.centery - self.target.rect.centery
        if (dx != 0):
            l = float(dy) / dx
            speed_x = math.sqrt((float(speed ** 2) / float(1 + l ** 2)))
            speed_y = abs(l) * speed_x
            if (dx > 0):
                speed_x = -speed_x
            if (dy > 0):
                speed_y = -speed_y
        else:
            speed_x = 0
            if (dy > 0):
                speed_y = -speed
            else:
                speed_y = speed
        return [speed_x,speed_y]
                
    def detect_lock(self):
        if (self.attack_area is None):
            return
        if (self.target is None):
            nearest_distance = -1
            self.attack_area.center = self.rect.center
            for p in self.players:
                if (p.rect.colliderect(self.attack_area)):
                    cur_distance = self.get_distance(p.rect)
                    if (nearest_distance < 0 or cur_distance < nearest_distance):
                        nearest_distance = cur_distance
                        self.target = p

    def crashed(self):
        self.explos_sound.play()
        self.kill()
        effect.spark_obj(self.rect.center)
#        flame.explosion(self.rect.center)
        #flame...
        item.build_item(self.item_type, self.screen_size, self.rect.center)
        

    def destroied(self, opponent): #called by the shot that hit the obj
        if opponent.power > 0:
            self.hp -= opponent.power
            if (self.hp <= 0):
                if (opponent.belong_to):
                    opponent.belong_to.score += self.score
                    opponent.belong_to.exp += self.exp
                    opponent.belong_to.exp_update()

                    #-------------------------------------------
                    cur_mp_time_scale = opponent.belong_to.mp_time_scale
                    #print cur_mp_time_scale
                    opponent.belong_to.mp += 1 + (1 + cur_mp_time_scale*0.02)**2
                    opponent.belong_to.mp_time_scale = 100

                    if opponent.belong_to.mp >= 100:
                        opponent.belong_to.mp = 0
                        if opponent.belong_to.mp_num < 3: 
                            opponent.belong_to.mp_num += 1
                    #-------------------------------------------

#            print "Player ",opponent.belong_to.player_num," score: ",opponent.belong_to.score," exp: ",opponent.belong_to.exp
                self.crashed()
        else:
            if (opponent.belong_to):
                opponent.belong_to.score += self.score
                opponent.belong_to.exp += self.exp
                opponent.belong_to.exp_update()
            self.crashed()

    def move_forward(self):
        if (self.rect.top + self.speed >= self.screen_height):
            self.kill()
            self.hp = 0
        else:
            self.rect = self.rect.move(0, self.speed)

    def move_sin(self):
        MAX_MOVE_RANGE = 7
        if (self.rect.top + self.speed >= self.screen_height):
            self.kill()
            self.hp = 0
        else:
            x_speed = MAX_MOVE_RANGE * math.sin(self.step_counter) / 2
            self.rect = self.rect.move(x_speed, self.speed)
            self.step_counter += 0.05

    def move_minus_sin(self):
        MAX_MOVE_RANGE = 7
        if (self.rect.top + self.speed >= self.screen_height):
            self.kill()
            self.hp = 0
        else:
            x_speed = - MAX_MOVE_RANGE * math.sin(self.step_counter) / 2
            self.rect = self.rect.move(x_speed, self.speed)
            self.step_counter += 0.05

    def move_cos(self):
        MAX_MOVE_RANGE = 7
        if (self.rect.top + self.speed >= self.screen_height):
            self.kill()
            self.hp = 0
        else:
            x_speed = MAX_MOVE_RANGE * math.cos(self.step_counter) / 2
            self.rect = self.rect.move(x_speed, self.speed)
            self.step_counter += 0.05

    def move_minus_cos(self):
        MAX_MOVE_RANGE = 7
        if (self.rect.top + self.speed >= self.screen_height):
            self.kill()
            self.hp = 0
        else:
            x_speed = - MAX_MOVE_RANGE * math.cos(self.step_counter) / 2
            self.rect = self.rect.move(x_speed, self.speed)
            self.step_counter += 0.05

    def move_tracing(self):
        if (self.target is None or not self.target.is_alived()):
            if (self.rect.top + self.speed >= self.screen_height):
                self.kill()
                self.hp = 0
            else:
                self.rect = self.rect.move(0, self.speed)
        else:
            if (self.rect.top + self.speed >= self.screen_height):
                self.kill()
                self.hp = 0
            else:
                speed_x,speed_y = self.get_shot_speed()
                if (self.rect.centery <= self.target.rect.centery + self.KEEP_DISTANCE):
                    if (self.rect.centerx + speed_x <= (self.target.rect.centerx - self.KEEP_DISTANCE)):
                        self.rect = self.rect.move(speed_x, speed_y)
                    elif (self.rect.centerx + speed_x >= (self.target.rect.centerx + self.KEEP_DISTANCE)):
                        self.rect = self.rect.move(speed_x, speed_y)
                    self.rect = self.rect.move(0,5)
                else:
                    self.rect = self.rect.move(0,1)
                    
                self.angle = collidet.angle_to_degree(-collidet.get_angle(self.rect.centerx - self.target.rect.centerx,\
                                        self.rect.centery - self.target.rect.centery))
                                        
                self.offset_x, self.offset_y = collidet.get_offsets(self.image.get_height() / 2.0, self.angle)

    def move_t(self):
        MINIX = 128
        MAXIX = 384
        if (self.move_step):
            if (self.rect.top + self.speed >= self.screen_height):
                self.kill()
                self.hp = 0
            else:
                self.rect = self.rect.move(0, self.speed)
            self.move_step -= 1
        else:
            if (self.speed >= 0):
                if (self.rect.left - self.speed >= MINIX):
                    self.rect.left -= self.speed
                else:
                    self.rect.left = MINIX
                    self.speed = -self.speed
            else:
                if (self.rect.right - self.speed <= MAXIX):
                    self.rect.right -= self.speed
                else:
                    self.rect.right = MAXIX
                    self.speed = -self.speed
                

    def move(self):
        if (self.motion == self.MOTION_STRAINGHT):  # move forward
            self.move_forward()

        elif (self.motion == self.MOTION_SIN):    # move controled by sin
            self.move_sin()

        elif (self.motion == self.MOTION_MINUS_SIN):    # move controled by -sin
            self.move_minus_sin()

        elif (self.motion == self.MOTION_COS):    # move controled by cos
            self.move_cos()

        elif (self.motion == self.MOTION_MINUS_COS):    # move controled by -cos
            self.move_minus_cos()

        elif (self.motion == self.MOTION_TRACING):    # Tracing movment
            self.move_tracing()

        elif (self.motion == self.MOTION_T):   # 'T' moving
            self.move_t()
                
        else:
            pass

    def draw(self, screen):
        screen.blit(self.image, self.rect)

    def is_alived(self):
        return self.hp > 0

    def isbossed(self):
        return self.isboss

    def get_distance(self, obj_rect):
        return math.sqrt(abs(self.rect.centerx - obj_rect.centerx) ** 2
                         + abs(self.rect.centery - obj_rect.centery) ** 2)

    def rotate(self):       # Useless yet...
        center = self.rect.center
        self.image = pygame.transform.rotozoom(self.image, self.angle, 1.0)
        self.rect = self.image.get_rect()
        self.rect.center = center

    def update(self):
        if (self.flame_spark_intv == 0):
            self.flame_spark_intv = self.SPARK_INTV
            self.image = self.accl_image
        else:
            self.flame_spark_intv -= 1
            self.image = self.norm_image
            
        if (self.angle):
            self.rotate()

        self.move()

        self.detect_lock()

        self.attack()

    #------------------ BUILDING FUNCTIONS -------------------

    def build_enemy_1(self):
        self.hp = 10
        self.score = 100
        self.exp = 50
        self.attack_mode = 0
        self.attack_area = None
        self.norm_image = pygame.image.load("data/images/e1.png")
        self.accl_image = pygame.image.load("data/images/e1accel.png")
        self.image = self.norm_image
        self.rect = self.image.get_rect(center = (self.init_pos))
        self.rect = self.rect.move(0, -self.rect.height / 2)
        self.speed = 4
        
        self.explos_sound = pygame.mixer.Sound("data/sounds/explos_enemy.wav")
        self.explos_sound.set_volume(1.0)
        
        self.power = self.hp
        

    def build_enemy_2(self):
        self.hp = 20
        self.score = 200
        self.exp = 100
        self.attack_mode = self.ATTACK_MODE_STRAIGHT_SINGLE                # Straight forward singal ball
        self.attack_area = None         
        self.norm_image = pygame.image.load("data/images/e2.png")
        self.accl_image = pygame.image.load("data/images/e2accel.png")
        self.fire_intv = 80
        self.firing = 20
        self.image = self.norm_image
        self.rect = self.image.get_rect(center = (self.init_pos))
        self.rect = self.rect.move(0, -self.rect.height / 2)
        self.speed = 4
        
        self.fire_sound = pygame.mixer.Sound("data/sounds/f_ball_enemy.wav")
        self.fire_sound.set_volume(0.5)
        
        self.explos_sound = pygame.mixer.Sound("data/sounds/explos_enemy.wav")
        self.explos_sound.set_volume(1.0)
        
        self.power = self.hp


    def build_enemy_3(self):
        self.hp = 5
        self.score = 50
        self.exp = 25
        self.attack_mode = self.ATTACK_MODE_STRAIGHT_DOUBLE                # Straight forward double ball
        self.attack_area = None
        self.fire_intv = 80
        self.firing = 20
        self.norm_image = pygame.image.load("data/images/e3.png")
        self.accl_image = pygame.image.load("data/images/e3accel.png")
        self.image = self.norm_image
        self.rect = self.image.get_rect(center = (self.init_pos))
        self.rect = self.rect.move(0, -self.rect.height / 2)
        self.speed = 4
        
        self.fire_sound = pygame.mixer.Sound("data/sounds/f_ball_enemy.wav")
        self.fire_sound.set_volume(0.5)
        
        self.explos_sound = pygame.mixer.Sound("data/sounds/explos_enemy.wav")
        self.explos_sound.set_volume(1.0)
        
        self.power = self.hp
        

    def build_enemy_4(self):
        self.hp = 5
        self.score = 60
        self.exp = 30
        self.attack_mode = self.ATTACK_MODE_STRAIGHT_THREE                # Straight 3 
        self.attack_area = pygame.Rect(0, 0, 600, 600)      # create an attack area
        self.attack_area.center = self.init_pos
        self.fire_intv = 80
        self.firing = 20
        self.norm_image = pygame.image.load("data/images/e4.png")
        self.accl_image = pygame.image.load("data/images/e4accel.png")
        self.image = self.norm_image
        self.rect = self.image.get_rect(center = (self.init_pos))
        self.rect = self.rect.move(0, -self.rect.height / 2)
        self.speed = 4
        
        self.fire_sound = pygame.mixer.Sound("data/sounds/f_ball_enemy.wav")
        self.fire_sound.set_volume(0.5)
        
        self.explos_sound = pygame.mixer.Sound("data/sounds/explos_enemy.wav")
        self.explos_sound.set_volume(1.0)
        
        self.power = self.hp
        

    def build_enemy_5(self):
        self.hp = 5
        self.score = 70
        self.exp = 35
        self.attack_mode = self.ATTACK_MODE_AIMING_NEAREST_SINGLE                # Aiming singal ball
        self.attack_area = pygame.Rect(0, 0, 600, 600)
        self.attack_area.center = self.init_pos
        self.fire_intv = 80
        self.firing = 20
        self.norm_image = pygame.image.load("data/images/e5.png")
        self.accl_image = pygame.image.load("data/images/e5accel.png")
        self.image = self.norm_image
        self.rect = self.image.get_rect(center = (self.init_pos))
        self.rect = self.rect.move(0, -self.rect.height / 2)
        self.speed = 4
        
        self.fire_sound = pygame.mixer.Sound("data/sounds/f_ball_enemy.wav")
        self.fire_sound.set_volume(0.5)
        
        self.explos_sound = pygame.mixer.Sound("data/sounds/explos_enemy.wav")
        self.explos_sound.set_volume(1.0)
        
        self.power = self.hp
        

    def build_enemy_20(self):
        self.hp = 100
        self.score = 2000
        self.exp = 1000
        self.attack_mode = self.ATTACK_MODE_BOSS_1                # Boss attack mode 1#
        self.attack_area = pygame.Rect(0, 0, 600, 600)
        self.attack_area.center = self.init_pos
        self.fire_intv = 80
        self.firing = self.fire_intv
        self.norm_image = pygame.image.load("data/images/boss1.png")
        self.accl_image = pygame.image.load("data/images/boss1accel.png")
        self.image = self.norm_image
        self.rect = self.image.get_rect(center = (self.init_pos))
        self.rect = self.rect.move(0, -self.rect.height / 2)

        self.move_step = 60                                         # steps number that the obj will keep moving forward
        self.isboss = True
        
        self.fire_sound = pygame.mixer.Sound("data/sounds/f_ball_enemy.wav")
        self.fire_sound.set_volume(0.5)
        
        self.explos_sound = pygame.mixer.Sound("data/sounds/explos_boss.wav")
        self.explos_sound.set_volume(1.0)
        
        self.power = self.hp
        

        #---------------------additional features------------------------
        self.fire_combo = 15
        self.combo = self.fire_combo
        self.fire_stage = 1
        self.fire_stage_limit = 3
        #---------------------additional features------------------------
            
        self.speed = 4

    def build_enemy_21(self):
        self.hp = 200
        self.score = 4000
        self.exp = 2000
        self.attack_mode = self.ATTACK_MODE_BOSS_2                # Boss attack mode 1#
        self.attack_area = pygame.Rect(0, 0, 600, 600)
        self.attack_area.center = self.init_pos
        self.fire_intv = 80
        self.firing = self.fire_intv
        self.norm_image = pygame.image.load("data/images/boss2.png")
        self.accl_image = pygame.image.load("data/images/boss2accel.png")
        self.image = self.norm_image
        self.rect = self.image.get_rect(center = (self.init_pos))
        self.rect = self.rect.move(0, -self.rect.height / 2)

        self.move_step = 60                                         # steps number that the obj will keep moving forward
        self.isboss = True
        
        self.fire_sound = pygame.mixer.Sound("data/sounds/f_ball_enemy.wav")
        self.fire_sound.set_volume(0.5)
        
        self.explos_sound = pygame.mixer.Sound("data/sounds/explos_boss.wav")
        self.explos_sound.set_volume(1.0)
        
        self.power = self.hp
        

        #---------------------additional features------------------------
        self.fire_combo = 15
        self.combo = self.fire_combo
        self.fire_stage = 1
        self.fire_stage_limit = 3
        #---------------------additional features------------------------
            
        self.speed = 4
        

    #----------------------ATTACKING FUNCTIONS ------------------------------

    def attack_strainght_single(self):
        self.firing = self.fire_intv
        
        self.fire_sound.play()
        
        weapon.enemy_shot_obj(self.screen_size, 0, 6.5, (self.rect.centerx,self.rect.centery),\
                              self, weapon.enemy_shot_obj.GREEN_BALL)

    def attack_strainght_double(self):
        self.firing = self.fire_intv
        
        self.fire_sound.play()
        
        weapon.enemy_shot_obj(self.screen_size, 0, 6.5, (self.rect.centerx - 10,self.rect.centery),\
                              self, weapon.enemy_shot_obj.GREEN_BALL)
        weapon.enemy_shot_obj(self.screen_size, 0, 6.5, (self.rect.centerx + 10,self.rect.centery),\
                              self, weapon.enemy_shot_obj.GREEN_BALL)

    def attack_strainght_three(self):
        self.firing = self.fire_intv
        
        self.fire_sound.play()
        
        weapon.enemy_shot_obj(self.screen_size, -1, 6.5, (self.rect.centerx,self.rect.centery),\
                              self, weapon.enemy_shot_obj.GREEN_BALL)
        weapon.enemy_shot_obj(self.screen_size, 0, 6.5, (self.rect.centerx,self.rect.centery),\
                              self, weapon.enemy_shot_obj.GREEN_BALL)
        weapon.enemy_shot_obj(self.screen_size, 1, 6.5, (self.rect.centerx,self.rect.centery),\
                              self, weapon.enemy_shot_obj.GREEN_BALL)

    def attack_aiming_single(self):
        if (self.target and self.target.is_alived()):
            speed_x,speed_y = self.get_shot_speed()
            self.firing = self.fire_intv
            
            self.fire_sound.play()
            
            weapon.enemy_shot_obj(self.screen_size, speed_x, speed_y,\
                (self.rect.centerx, self.rect.centery), self, weapon.enemy_shot_obj.GREEN_BALL)
                

    def attack_aiming_nearest_single(self):
        if (self.target and self.target.is_alived()):
            self.get_nearest_player()
            speed_x,speed_y = self.get_shot_speed()
        
            self.fire_sound.play()
            
            weapon.enemy_shot_obj(self.screen_size, speed_x, speed_y,\
                (self.rect.centerx, self.rect.centery), self, weapon.enemy_shot_obj.GREEN_BALL)
            
        

    def attack_boss_1(self):
        self.get_nearest_player()
        speed_x,speed_y = self.get_shot_speed()
        if (self.combo):
            self.fire_sound.play()
            weapon.enemy_shot_obj(self.screen_size, speed_x, speed_y, (self.rect.centerx - 10,self.rect.centery),\
                                  self, weapon.enemy_shot_obj.RED_BALL)
            weapon.enemy_shot_obj(self.screen_size, -speed_x, speed_y, (self.rect.centerx + 10,self.rect.centery),\
                                  self, weapon.enemy_shot_obj.RED_BALL)
            self.combo -= 1
            if self.combo == 0:
                self.firing = self.fire_intv
                self.combo = self.fire_combo
            else:
                self.firing = 4

    def attack_boss_2(self):
        self.get_nearest_player()
        speed_x,speed_y = self.get_shot_speed()
        if (self.combo):
            tsx1,tsy1 = collidet.trans_dx_dy_angle(speed_x,speed_y, math.pi / 6.0)
            tsx11,tsy11 = collidet.trans_dx_dy_angle(tsx1,tsy1, math.pi / 6.0)
            tsx2,tsy2 = collidet.trans_dx_dy_angle(speed_x,speed_y, - math.pi / 6.0)
            tsx22,tsy22 = collidet.trans_dx_dy_angle(tsx2,tsy2, - math.pi / 6.0)
            
            self.fire_sound.play()
            
            weapon.enemy_shot_obj(self.screen_size, tsx11, tsy11, (self.rect.centerx - 10,self.rect.centery),\
                                    self,  weapon.enemy_shot_obj.RED_BALL)
            weapon.enemy_shot_obj(self.screen_size, speed_x, speed_y, (self.rect.centerx - 10,self.rect.centery),\
                                    self,  weapon.enemy_shot_obj.RED_BALL)
            weapon.enemy_shot_obj(self.screen_size, tsx22, tsy22, (self.rect.centerx - 10,self.rect.centery),\
                                    self,  weapon.enemy_shot_obj.RED_BALL)
            weapon.enemy_shot_obj(self.screen_size, -tsx11, tsy11, (self.rect.centerx + 10,self.rect.centery),\
                                    self,  weapon.enemy_shot_obj.RED_BALL)
            weapon.enemy_shot_obj(self.screen_size, -speed_x, speed_y, (self.rect.centerx + 10,self.rect.centery),\
                                    self,  weapon.enemy_shot_obj.RED_BALL)
            weapon.enemy_shot_obj(self.screen_size, -tsx22, tsy22, (self.rect.centerx + 10,self.rect.centery),\
                                    self,  weapon.enemy_shot_obj.RED_BALL)
                                    
            self.combo -= 1
            
            if self.combo == 0:
                self.firing = self.fire_intv
                self.combo = self.fire_combo
            else:
                self.firing = 4
